/*
 * FlowLayout.java  0.4.0 / Oct 24, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing.layouts;


import com.cute4j.swing.Component;
import com.cute4j.swing.Container;
import com.cute4j.swing.graphics.Dimension;
import com.cute4j.swing.graphics.Insets;
import com.cute4j.util.CommonConstants;

/**
 * Flow Layout.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class FlowLayout implements LayoutManager {

// Public Static Fields. -------------------------------------------------------

  /**
   * <tt>Left</tt> layout alignment (the value is {@value}).
   */
  public static final int LEFT = 0;

  /**
   * <tt>Center</tt> layout alignment (the value is {@value}).
   */
  public static final int CENTER = 1;

  /**
   * <tt>Right</tt> layout alignment (the value is {@value}).
   */
  public static final int RIGHT = 2;

  /**
   * <tt>Leading</tt> layout alignment (the value is {@value}).
   */
  public static final int LEADING = 3;

  /**
   * <tt>Trailing</tt> layout alignment (the value is {@value}).
   */
  public static final int TRAILING = 4;

// Private Fields. -------------------------------------------------------------

  private int align;
  private int hgap; // TODO: rename ?
  private int vgap; // TODO: rename ?

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new FlowLayout object.
   *
   * @param align the alignment value
   * @param hgap the horizontal gap between components
   * @param vgap the vertical gap between components
   */
  public FlowLayout(int align, int hgap, int vgap) {
    this.hgap = hgap;
    this.vgap = vgap;
    this.align = align;
  }

  /**
   * Constructs new FlowLayout object.
   *
   * @param align the alignment value
   */
  public FlowLayout(int align) {
    this(align, 5, 5);
  }

  /**
   * Constructs new FlowLayout object.
   */
  public FlowLayout() {
    this(CENTER, 5, 5);
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the alignment for this layout.
   *
   * @return the alignment for this layout
   */
  public int getAlignment() {
    return align;
  }

  /**
   * Sets the alignment for this layout.
   *
   * @param alignment the alignment
   */
  public void setAlignment(int alignment) {
    this.align = alignment;
  }

  /**
   * Returns the horizontal gap between components.
   *
   * @return the horizontal gap between components
   */
  public int getHgap() {
    return hgap;
  }

  /**
   * Sets the horizontal gap between components.
   *
   * @param hgap the horizontal gap between components
   */
  public void setHgap(int hgap) {
    this.hgap = hgap;
  }

  /**
   * Returns the vertical gap between components.
   *
   * @return the vertical gap between components
   */
  public int getVgap() {
    return vgap;
  }

  /**
   * Sets the vertical gap between components.
   *
   * @param vgap the vertical gap between components
   */
  public void setVgap(int vgap) {
    this.vgap = vgap;
  }

// LayoutManager Interface Implementation. -------------------------------------

  /**
   * {@inheritDoc}
   */
  public void addLayoutComponent(Component comp, Object constraints) {
    // empty implementation
  }

  /**
   * {@inheritDoc}
   */
  public void removeLayoutComponent(Component comp) {
    // empty implementation
  }

  /**
   * {@inheritDoc}
   */
  public Dimension preferredLayoutSize(Container target) {
    int width = 0;
    int height = 0;
    int componentCount = target.getComponentCount();
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getPreferredSize();
        int dh = d.getHeight();
        if (height < d.getHeight()) {
          height = dh;
        }
        width += hgap + d.getWidth();
      }
    }
    width += hgap;
    if (componentCount == 0) {
      width += hgap;
    }
    Insets insets = target.getInsets();
    return new Dimension(width + insets.getLeft() + insets.getRight(),
                         height + insets.getTop() + insets.getBottom() +
                         (vgap << 1));
  }

  /**
   * {@inheritDoc}
   */
  public Dimension maximumLayoutSize(Container target) {
    int width = 0;
    int height = 0;
    int componentCount = target.getComponentCount();
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getMaximumSize();
        int dh = d.getHeight();
        if (height < d.getHeight()) {
          height = dh;
        }
        width += hgap + d.getWidth();
      }
    }
    width += hgap;
    if (componentCount == 0) {
      width += hgap;
    }
    Insets insets = target.getInsets();
    return new Dimension(width + insets.getLeft() + insets.getRight(),
                         height + insets.getTop() + insets.getBottom() +
                         (vgap << 1));
  }

  /**
   * {@inheritDoc}
   */
  public Dimension minimumLayoutSize(Container target) {
    int width = 0;
    int height = 0;
    int componentCount = target.getComponentCount();
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getMinimumSize();
        int dh = d.getHeight();
        if (height < d.getHeight()) {
          height = dh;
        }
        width += hgap + d.getWidth();
      }
    }
    width += hgap;
    if (componentCount == 0) {
      width += hgap;
    }
    Insets insets = target.getInsets();
    return new Dimension(width + insets.getLeft() + insets.getRight(),
                         height + insets.getTop() + insets.getBottom() +
                         (vgap << 1));
  }

  /**
   * {@inheritDoc}
   */

  public void layoutContainer(Container target) {
    Insets insets = target.getInsets();
    int maxWidth = target.getWidth() - insets.getLeft() - insets.getRight() -
                   (hgap << 1);
    int x = 0;
    int y = insets.getTop() + vgap;
    int rowHeight = 0;
    int start = 0;
    int componentCount = target.getComponentCount();
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getPreferredSize();
        component.setSize(d.getWidth(), d.getHeight());
        if ((x == 0) || ((x + d.getWidth()) <= maxWidth)) {
          if (x > 0) {
            x += hgap;
          }
          x += d.getWidth();
          if (d.getHeight() > rowHeight) {
            rowHeight = d.getHeight();
          }
        } else {
          moveComponents(target, insets.getLeft() + hgap, y, maxWidth - x,
                         rowHeight, start, i);
          x = d.getWidth();
          y += vgap + rowHeight;
          rowHeight = d.getHeight();
          start = i;
        }
      }
    }
    moveComponents(target, insets.getLeft() + hgap, y, maxWidth - x, rowHeight,
                   start, componentCount);
  }

  /**
   * {@inheritDoc}
   */
  public void invalidateLayout(Container target) {
    // empty method body
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public String toString() {
    String alignString;
    switch (align) {
      case LEFT:
        alignString = ",align=left";
        break;
      case CENTER:
        alignString = ",align=center";
        break;
      case RIGHT:
        alignString = ",align=right";
        break;
      case LEADING:
        alignString = ",align=leading";
        break;
      case TRAILING:
        alignString = ",align=trailing";
        break;
      default:
        alignString = CommonConstants.STRING_EMPTY;
    }
    return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap +
           alignString + "]";
  }

// Private Methods. ------------------------------------------------------------

  private void moveComponents(Container target, int x, int y, // NOPMD
                              int width, int height, int rowStart, int rowEnd) {
    switch (align) { // NOPMD
      case CENTER:
        x += (width >> 1);
        break;
      case RIGHT:
        // falls through
      case TRAILING:
        x += width;
        break;
    }
    for (int i = rowStart; i < rowEnd; i++) {
      Component comp = target.getComponent(i);
      if (comp.isDisplayable()) {
        comp.setLocation(x, y + ((height - comp.getHeight()) >> 1));
        x += comp.getWidth() + hgap;
      }
    }
  }

}
